package LeetCode.SlideWindow;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author : LdLtd
 * @Date : 2023/7/13
 * @Description:3. 无重复字符的最长子串
 * 给定一个字符串 s ，请你找出其中不含有重复字符的 最长子串 的长度。
 *
 *  
 *
 * 示例 1:
 *
 * 输入: s = "abcabcbb"
 * 输出: 3
 * 解释: 因为无重复字符的最长子串是 "abc"，所以其长度为 3。
 * 示例 2:
 *
 * 输入: s = "bbbbb"
 * 输出: 1
 * 解释: 因为无重复字符的最长子串是 "b"，所以其长度为 1。
 * 示例 3:
 *
 * 输入: s = "pwwkew"
 * 输出: 3
 * 解释: 因为无重复字符的最长子串是 "wke"，所以其长度为 3。
 *      请注意，你的答案必须是 子串 的长度，"pwke" 是一个子序列，不是子串。

 */
public class longest_substring_without_repeating_characters {
    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.lengthOfLongestSubstring1("pwwkew"));
    }
    static class Solution {
        /*时间长*/
        public int lengthOfLongestSubstring1(String s) {
            int res=0,t=0;
            List<Character> reslist = new ArrayList<>();
            for (int i = 0; i < s.length(); i++) {
                //如果包含了s总的字母
                if(reslist.contains((s.charAt(i)))){
                    //如果在最后一个位置，直接清空
                    if(reslist.size()==reslist.indexOf(s.charAt(i))) reslist.clear();
                    //否则就从新的重复的位置重新开始计算长度  比如abb直接清空  aba则从a开始计算
                    else reslist=reslist.subList(reslist.indexOf(s.charAt(i))+1,reslist.size());
                }
                reslist.add(s.charAt(i));
                res=Math.max(res,reslist.size());
            }
            return res;
        }


        /*最简*/
        public int lengthOfLongestSubstring2(String s) {
            int n = s.length();
            //记录上一次字符出现的位置
            int[] last = new int[128];
            int res = 0;
            int start = 0; // 窗口开始位置
            for(int i = 0; i < n; i++) {
                //记录这个字母的ascii码
                int index = s.charAt(i);
                //左端点
                start = Math.max(start, last[index]);
                //跟新结果
                res   = Math.max(res, i - start + 1);
                //更新字符位置
                last[index] = i+1;
            }

            return res;
        }
public int lengthOfLongestSubstring3(String s) {
    HashMap<Character, Integer> map = new HashMap<>();
    int maxLen = 0;//用于记录最大不重复子串的长度
    int left = 0;//滑动窗口左指针
    for (int i = 0; i < s.length() ; i++)
    {
        /**
         1、首先，判断当前字符是否包含在map中，如果不包含，将该字符添加到map（字符，字符在数组下标）,
         此时没有出现重复的字符，左指针不需要变化。此时不重复子串的长度为：i-left+1，与原来的maxLen比较，取最大值；

         2、如果当前字符 ch 包含在 map中，此时有2类情况：
         1）当前字符包含在当前有效的子段中，如：abca，当我们遍历到第二个a，当前有效最长子段是 abc，我们又遍历到a，
         那么此时更新 left 为 map.get(a)+1=1，当前有效子段更新为 bca；
         2）当前字符不包含在当前最长有效子段中，如：abba，我们先添加a,b进map，此时left=0，我们再添加b，发现map中包含b，
         而且b包含在最长有效子段中，就是1）的情况，我们更新 left=map.get(b)+1=2，此时子段更新为 b，而且map中仍然包含a，map.get(a)=0；
         随后，我们遍历到a，发现a包含在map中，且map.get(a)=0，如果我们像1）一样处理，就会发现 left=map.get(a)+1=1，实际上，left此时
         应该不变，left始终为2，子段变成 ba才对。

         为了处理以上2类情况，我们每次更新left，left=Math.max(left , map.get(ch)+1).
         另外，更新left后，不管原来的 s.charAt(i) 是否在最长子段中，我们都要将 s.charAt(i) 的位置更新为当前的i，
         因此此时新的 s.charAt(i) 已经进入到 当前最长的子段中！
         */
        if(map.containsKey(s.charAt(i)))
        {
            left = Math.max(left , map.get(s.charAt(i))+1);
        }
        //不管是否更新left，都要更新 s.charAt(i) 的位置！
        map.put(s.charAt(i) , i);
        maxLen = Math.max(maxLen , i-left+1);
    }

    return maxLen;
}
    }
}
